using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using Engine;
using GameEntitySystem;
using TemplatesDatabase;
using XmlUtilities;

namespace Game
{
	[Extension]
	public static class ModUtils
	{
		public static void ChangeState(StateMachine machine, string name, Action enter, Action update, Action leave)
		{
			ReflectionUtils.InvokeMethod(ReflectionUtils.GetField((object)machine, "m_states"), "Remove", new object[1] { name });
			machine.AddState(name, enter, update, leave);
		}

		public static void AddScreen(string name, Screen s)
		{
			ScreensManager.AddScreen(name, s);
		}

		public static void ReplaceScreen(string name, Screen s)
		{
			(ReflectionUtils.GetStaticField(typeof(ScreensManager), "m_screens") as Dictionary<string, Screen>)[name] = s;
		}

		public static void AddLoadAction(Action action)
		{
			ScreensManager.FindScreen<LoadingScreen>("Loading").AddLoadAction(action);
		}

		public static void AddDatabaseObjects(XElement node, bool generateNewGuids = false)
		{
			foreach (DatabaseObject item in XmlDatabaseSerializer.LoadDatabaseObjectsList(node, DatabaseManager.GameDatabase.Database, generateNewGuids))
			{
				item.NestingParent = DatabaseManager.GameDatabase.Database.Root;
			}
		}

		public static DatabaseObject AddSubsystem(string projectName, Type type, string dependencies = null)
		{
			GameDatabase gameDatabase = DatabaseManager.GameDatabase;
			DatabaseObject nestingParent = gameDatabase.Database.FindDatabaseObject(projectName, gameDatabase.ProjectTemplateType, true);
			DatabaseObject databaseObject = new DatabaseObject(gameDatabase.MemberSubsystemTemplateType, type.Name);
			databaseObject.ExplicitInheritanceParent = gameDatabase.Database.FindDatabaseObject(new Guid("a3914c13-7801-46a6-b23a-9e2b36e9defb"), gameDatabase.SubsystemTemplateType, true);
			databaseObject.SetNestedValue("Class", type.FullName);
			if (dependencies != null)
			{
				AddSubsystemDependencies(databaseObject, dependencies);
			}
			databaseObject.NestingParent = nestingParent;
			return databaseObject;
		}

		public static DatabaseObject AddSubsystemWithTemplate(string projectName, string name)
		{
			GameDatabase gameDatabase = DatabaseManager.GameDatabase;
			DatabaseObject nestingParent = gameDatabase.Database.FindDatabaseObject(projectName, gameDatabase.ProjectTemplateType, true);
			DatabaseObject databaseObject = gameDatabase.Database.FindDatabaseObject(name, gameDatabase.SubsystemTemplateType, true);
			DatabaseObject databaseObject2 = new DatabaseObject(gameDatabase.MemberSubsystemTemplateType, databaseObject.Name);
			databaseObject2.ExplicitInheritanceParent = databaseObject;
			databaseObject2.NestingParent = nestingParent;
			return databaseObject2;
		}

		private static void AddSubsystemDependencies(DatabaseObject obj, string dependencies)
		{
			string nestedValue = obj.GetNestedValue<string>("Dependencies");
			if (string.IsNullOrEmpty(nestedValue))
			{
				obj.SetNestedValue("Dependencies", dependencies);
			}
			else
			{
				obj.SetNestedValue("Dependencies", nestedValue + ", " + dependencies);
			}
		}

		public static DatabaseObject ReplaceSubsystem(Type oldSubsystem, Type newSubystem, string newDependencies = null)
		{
			GameDatabase gameDatabase = DatabaseManager.GameDatabase;
			foreach (DatabaseObject effectiveNestingChild in gameDatabase.Database.Root.GetEffectiveNestingChildren(gameDatabase.SubsystemTemplateType, false))
			{
				if (effectiveNestingChild.GetNestedValue<string>("Class") == oldSubsystem.FullName)
				{
					effectiveNestingChild.SetNestedValue("Class", newSubystem.FullName);
					if (newDependencies != null)
					{
						AddSubsystemDependencies(effectiveNestingChild, newDependencies);
					}
					return effectiveNestingChild;
				}
			}
			throw new Exception("Cannot find subsystem: " + (((object)oldSubsystem != null) ? oldSubsystem.ToString() : null));
		}

		public static DatabaseObject AddComponentToEntity(string entity, Type newComponent)
		{
			GameDatabase gameDatabase = DatabaseManager.GameDatabase;
			DatabaseObject nestingParent = gameDatabase.Database.FindDatabaseObject(entity, gameDatabase.EntityTemplateType, true);
			DatabaseObject databaseObject = new DatabaseObject(gameDatabase.MemberComponentTemplateType, newComponent.Name);
			databaseObject.ExplicitInheritanceParent = gameDatabase.Database.FindDatabaseObject(new Guid("b05700ed-7e4e-4679-98f5-b597f421496b"), gameDatabase.ComponentTemplateType, true);
			databaseObject.SetNestedValue("Class", newComponent.FullName);
			databaseObject.NestingParent = nestingParent;
			return databaseObject;
		}

		internal static void ReloadEntities()
		{
			Log.Information("Reloading entities");
			GameDatabase gameDatabase = DatabaseManager.GameDatabase;
			Dictionary<string, ValuesDictionary> dictionary = (Dictionary<string, ValuesDictionary>)ReflectionUtils.GetStaticField(typeof(DatabaseManager), "m_valueDictionaries");
			dictionary.Clear();
			foreach (DatabaseObject explicitNestingChild in gameDatabase.Database.Root.GetExplicitNestingChildren(gameDatabase.EntityTemplateType, false))
			{
				ValuesDictionary valuesDictionary = new ValuesDictionary();
				valuesDictionary.PopulateFromDatabaseObject(explicitNestingChild);
				dictionary.Add(explicitNestingChild.Name, valuesDictionary);
			}
		}

		public static void ChangeProjectGuid(WorldInfo worldInfo, string customProject)
		{
			string path = Storage.CombinePaths(worldInfo.DirectoryName, "Project.xml");
			XElement xElement;
			using (Stream stream = Storage.OpenFile(path, OpenFileMode.Read))
			{
				xElement = XmlUtils.LoadXmlFromStream(stream, null, true);
				xElement.SetAttributeValue("Name", customProject);
				xElement.SetAttributeValue("Guid", DatabaseManager.GameDatabase.Database.FindDatabaseObject(customProject, DatabaseManager.GameDatabase.ProjectTemplateType, true));
			}
			Storage.DeleteFile(path);
			using (Stream stream2 = Storage.OpenFile(path, OpenFileMode.Create))
			{
				XmlUtils.SaveXmlToStream(xElement, stream2, null, true);
			}
		}

		public static void LoadCustomProject(ValuesDictionary overrides, WorldInfo worldInfo, ContainerWidget gamesWidget)
		{
			using (Stream stream = Storage.OpenFile(Storage.CombinePaths(worldInfo.DirectoryName, "Project.xml"), OpenFileMode.Read))
			{
				ValuesDictionary valuesDictionary = new ValuesDictionary();
				ValuesDictionary valuesDictionary2 = new ValuesDictionary();
				valuesDictionary2.Add("WorldDirectoryName", worldInfo.DirectoryName);
				valuesDictionary.Add("GameInfo", valuesDictionary2);
				ValuesDictionary valuesDictionary3 = new ValuesDictionary();
				valuesDictionary3.Add("GamesWidget", gamesWidget);
				valuesDictionary.Add("Views", valuesDictionary3);
				overrides.ApplyOverrides(valuesDictionary);
				XElement projectNode = XmlUtils.LoadXmlFromStream(stream, null, true);
				ProjectData projectData = new ProjectData(DatabaseManager.GameDatabase, projectNode, overrides, true);
				ReflectionUtils.SetStaticField(typeof(GameManager), "m_project", (object)new Project(DatabaseManager.GameDatabase, projectData));
				ReflectionUtils.SetStaticField(typeof(GameManager), "m_subsystemUpdate", (object)GameManager.Project.FindSubsystem<SubsystemUpdate>(true));
			}
			ReflectionUtils.SetStaticField(typeof(GameManager), "m_worldInfo", (object)worldInfo);
			Log.Information("Loaded world, GameMode={0}, WorldName={1}, VisibilityRange={2}, Resolution={3}", worldInfo.WorldSettings.GameMode, worldInfo.WorldSettings.Name, SettingsManager.VisibilityRange.ToString(), SettingsManager.ResolutionMode.ToString());
			GC.Collect();
		}

		public static XElement CreateProject(string projectDatabaseName, ValuesDictionary gameInfos)
		{
			DatabaseObject databaseObject = DatabaseManager.GameDatabase.Database.FindDatabaseObject(projectDatabaseName, DatabaseManager.GameDatabase.ProjectTemplateType, true);
			XElement xElement = new XElement("Project");
			XmlUtils.SetAttributeValue(xElement, "Guid", databaseObject.Guid);
			XmlUtils.SetAttributeValue(xElement, "Name", projectDatabaseName);
			XmlUtils.SetAttributeValue(xElement, "Version", VersionsManager.SerializationVersion);
			XElement xElement2 = new XElement("Subsystems");
			xElement.Add(xElement2);
			XElement xElement3 = new XElement("Values");
			XmlUtils.SetAttributeValue(xElement3, "Name", "GameInfo");
			gameInfos.Save(xElement3);
			xElement2.Add(xElement3);
			return xElement;
		}

		[Extension]
		public static XElement LoadXmlFromAssembly(object obj, string name)
		{
			return LoadXmlFromAssembly(obj.GetType(), name);
		}

		public static XElement LoadXmlFromAssembly(Type type, string name)
		{
			string[] manifestResourceNames = type.Assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (text.EndsWith(name))
				{
					using (Stream stream = type.Assembly.GetManifestResourceStream(text))
					{
						return XmlUtils.LoadXmlFromStream(stream, null, true);
					}
				}
			}
			throw new Exception("cannot find resource in assembly: " + name);
		}
	}
}
